﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MapReduceLib.Configuration;
using MapReduceLib.Contracts;
using log4net;
using System.ServiceModel;
using System.Threading;

namespace MasterNode.ServiceImpl
{
    class Cluster
    {
        private List<ITaskExecuter> aliveSlaves;
        private ClusterConfig clusterConfig;
        private static ILog log = LogManager.GetLogger(typeof(Cluster));

        public Cluster(ClusterConfig clusterConfig)
        {
            this.clusterConfig = clusterConfig;
            aliveSlaves = new List<ITaskExecuter>();
            InitializeCluster();
        }

        private void InitializeCluster()
        {
            log.Debug("Initializing Cluster");

            foreach (string slaveAddr in clusterConfig.SlaveAddreses)
            {
                try
                {
                    log.DebugFormat("Contacting slave at addr {0}", slaveAddr);
                    EndpointAddress slaveEndpoint = new EndpointAddress(string.Format("net.tcp://{0}/TaskExecuter/", slaveAddr));
                    ChannelFactory<ITaskExecuter> taskTrackerFactory = new ChannelFactory<ITaskExecuter>(new NetTcpBinding(SecurityMode.None), slaveEndpoint);
                    ITaskExecuter taskExecuter = taskTrackerFactory.CreateChannel();
                    taskExecuter.PingRequest();
                    aliveSlaves.Add(taskExecuter);
                }
                catch (Exception e)
                {
                    log.Error(e.Message);
                }
            }

            log.DebugFormat("Starting slave status(ping) thread...");
            ThreadPool.QueueUserWorkItem(PingTask);
        }

        private void PingTask(object state)
        {
            List<ITaskExecuter> deadSlaves = new List<ITaskExecuter>();

            for (; ; )
            {
                Thread.Sleep(TimeSpan.FromSeconds(clusterConfig.PingInterval));

                log.Debug("Running ping job...");
                deadSlaves.Clear();
                foreach (ITaskExecuter slaveService in aliveSlaves)
                {
                    try
                    {
                        slaveService.PingRequest();
                    }
                    catch (Exception e)
                    {
                        log.Error(e.Message);
                        deadSlaves.Add(slaveService);
                    }
                }
                aliveSlaves.RemoveAll(x => deadSlaves.Contains(x));
            }
        }
    }
}
